
#include "cGame.h"
#include "cAutonomousAgent.h"
//#include "cSentinel.h"
#include "cTank.h"
#include "cSentinelNest.h"
#include "cItem.h"
#ifdef _WIN32
#include <gl\glut.h>
#elif __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#define INC_SPEED 2
#define INC_JUMP 10

char buffer[100];

cGame::cGame(void){
    nextGame=CENTRAL_GAME_ST;
}

cGame::~cGame(void){
    for(list<cScene*>::iterator it=SceneManager.begin(); it!=SceneManager.end(); it++){
        delete (*it);
    }
    for(list<cAutonomousAgent*>::iterator it=agentList.begin(); it!=agentList.end(); it++){
        delete (*it);
    }
    for(list<cPositionedVisibleObject*>::iterator it=itemList.begin(); it!=itemList.end(); it++){
        delete (*it);
    }
    for(list<cAgentNest*>::iterator it=AgentNestList.begin(); it!=AgentNestList.end(); it++){
        delete (*it);
    }
    delete Camera;
}

bool cGame::Init(cData* m_Data, cCamera* cam){
    bool res=true;

    Data = m_Data;

     //Graphics initialization
    glClearColor(0.0f,0.0f,0.0f,0.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0,CAMERA_WIDTH,0,CAMERA_HEIGHT,0,1);
    glMatrixMode(GL_MODELVIEW);
    //Alpha test enabled
    glAlphaFunc(GL_GREATER, 0.05f);
    glEnable(GL_ALPHA_TEST);
    //Textures enabled
    glEnable(GL_TEXTURE_2D);

    //Scene initialization
    //cBackgroundScene *Scene=new cBackgroundScene();
    cScene * Scene =new cBackgroundScene();
    ((cBackgroundScene*) Scene)->Init(Data->loadImage(CLOUDS_IMG, GL_RGB), true, true, 0.7, 0);
    SceneManager.push_back((cScene*&) Scene);

    Scene=new cBackgroundScene();
    ((cBackgroundScene*) Scene)->Init(Data->loadImage(BACKGROUND_IMG, GL_RGB),
                                                    true, false, 0, 0);
    SceneManager.push_back((cScene*&) Scene);

    Scene=new cBackgroundScene();
    ((cBackgroundScene*) Scene)->Init(Data->loadImage(_51A_IMG, GL_RGBA),
                                                    true, false, 0.4, 0, 0, 150);
    SceneManager.push_back((cScene*&) Scene);

    /*
    Scene=new cBackgroundScene();
    ((cBackgroundScene*) Scene)->Init(Data->loadImage("PecVJ2D_level1_v2_1.png", GL_RGBA),
                                                    false, false, 0, 0, 100, 0);
    SceneManager.push_back((cScene*&) Scene);
     */

    Scene = new cTiledScene();
    ((cTiledScene*) Scene)->InitFromIniFile(PECVJ2D_SCENE_LEVEL_CFG, Data);
    SceneManager.push_back((cScene*&) Scene);


    //inicializamos l mundo físico
    Physics.Init(((cTiledScene*) Scene)->GetScenWidth()
                            * ((cTiledScene*) Scene)->GetTileSize(),
            ((cTiledScene*) Scene)->GetSceneHeight()
                            * ((cTiledScene*) Scene)->GetTileSize(), 10);

    //player initialization
    Player.InitFromIniFile(CRITTER_CFG, Data, /* 9200/**/225/**/, 550);
    Player.SetMaxForceFactor(X, -1);
    Player.SetMaxForceFactor(Y, 4);
    Player.SetNom("Player");
    //Player.setItemCounter(FOOD_ITEM_INDEX, 5000);

    //Camera initialization
    cScrollCamera * sccamera = new cScrollCamera();
    sccamera->Init(&Player, CAMERA_WIDTH, CAMERA_HEIGHT, 1, 1);
    Camera = sccamera;
    sccamera->SetWorldDimension(X,((cTiledScene*) Scene)->GetScenWidth()
                            * ((cTiledScene*) Scene)->GetTileSize());
    sccamera->SetWorldDimension(Y,((cTiledScene*) Scene)->GetSceneHeight()
                            * ((cTiledScene*) Scene)->GetTileSize());

    ObjectMap.InitFromIniFile(PECVJ2D_OBJECTMAP_CFG, Data);
    itemList.assign(ObjectMap.GetItems()->begin(), ObjectMap.GetItems()->end());
    /*
    //añadir items
    itemList.push_back(new cItem());
    ((cItem*)itemList.back())->Init(Data.loadImage("monedas.png", GL_RGBA)
            , 47*((cTiledScene*) Scene)->GetTileSize(), 16*((cTiledScene*) Scene)->GetTileSize()
            , 0, 5, 16, 16);
     */

    // añadir personajes (agentes)

    agentList.push_back((cAutonomousAgent*) new cTank());
    agentList.back()->InitFromIniFile(TANK_CFG, Data, 9000, 1000);

    /*
    agentList.back()->SetSurfaceType(NORTH_SURFACE, SOFT_SURFACE);
    agentList.back()->SetSurfaceType(EAST_SURFACE, SOFT_SURFACE);
    agentList.back()->SetSurfaceType(WEST_SURFACE, SOFT_SURFACE);
    agentList.back()->SetSurfaceType(SOUTH_SURFACE, SOFT_SURFACE);
     */
    /**/
    agentList.back()->SetSurfaceType(NORTH_SURFACE, HARD_SURFACE);
    agentList.back()->SetSurfaceType(EAST_SURFACE, HARD_SURFACE);
    agentList.back()->SetSurfaceType(WEST_SURFACE, HARD_SURFACE);
    agentList.back()->SetSurfaceType(SOUTH_SURFACE, HARD_SURFACE);
     /**/
    agentList.back()->SetNom("Tanque");
    ((cTank*)(agentList.back()))->addEnemy(PLAYER_TYPE);
    ((cTank*)(agentList.back()))->SetBulletTexture(Data->loadImage(
                                                        BULLET_IMG, GL_RGBA));
    agentList.back()->on();


    AgentNestList.push_back(new cSentinelNest());
    ((cSentinelNest*)AgentNestList.back())->SetCamera(Camera);
    ((cSentinelNest*)AgentNestList.back())->SetData(Data);

    SceneManager.Init(Camera);

    //Passar los datos generador a la física
    Physics.SetPlayer(&Player);
    Physics.SetObjects((list<cPositionedObject*> *) &itemList);
    Physics.SetAgents((list<cPhysicalObject*> *) &agentList);
    Physics.SetScene((cTiledScene*) Scene);

    message.SetFont(GLUT_BITMAP_HELVETICA_10);
    message.SetSpacing(2);
    message.SetColor(1,1,1);

    
    time=glutGet(GLUT_ELAPSED_TIME);
    return res;
}

int cGame::Loop(){
	float elapsedTime = (glutGet(GLUT_ELAPSED_TIME)-time)/timeUnit;
        time = glutGet(GLUT_ELAPSED_TIME);

        Process();
        if(nextGame==CENTRAL_GAME_ST){
            //incorporar nuevos agentes a la escena
            for(list<cAgentNest*>::iterator it=AgentNestList.begin();
                    it!=AgentNestList.end(); it++){
                (*it)->NextStep(elapsedTime);
                if((*it)->hasChild()){
                    agentList.push_back((*it)->popChild());
                    agentList.back()->on();
                }
            }
            //Actualizar la física del player
            Physics.ProcessNextStep(&Player, elapsedTime);

            for(list<cPositionedVisibleObject*>::iterator it=itemList.begin(); it!=itemList.end(); it++){
                (*it)->NextStep(elapsedTime);
            }

            //actualizar la físca de todos los agentes
            for(list<cAutonomousAgent*>::iterator it=agentList.begin(); it!=agentList.end(); it++){
                Physics.ProcessNextStep((*it), elapsedTime);
                //SI HAN CREADO UN HIJO PONERLO MEN CIRCULACIÓN
                if((*it)->hasChild()){
                    agentList.push_back((cAutonomousAgent*) (*it)->popChild());
                    agentList.back()->on();
                }
            }

            if(Player.isDead()){
                if(Player.getLives()>0){
                    Player.revive();
                }else{
                    nextGame=LOSER_GAME_ST;
                }
            }else if(Player.GetPosX()>=CASTLE_DOOR_PS_X && Player.getItemCounter(KEY_ITEM_INDEX)>0){
                nextGame=WINNER_GAME_ST;
            }

            //eliminar los agentes que se encuentran lejos de la escena
            //TO DO....

            //eliminar lo que ya no sirve
	    list<cPositionedVisibleObject*>::iterator itp=itemList.begin();
	    while(itp!=itemList.end()){
                cPositionedVisibleObject* aux;
                if(!(*itp)->hasNextTime()){
                    aux=(*itp); 
                    delete (aux);
                    itp = itemList.erase(itp);
				}else{
					itp++;
                }
            }
	    list<cAutonomousAgent*>::iterator ita=agentList.begin();
            while(ita!=agentList.end()){
                cAutonomousAgent* aux;
                if(!(*ita)->hasNextTime()){
                    aux=(*ita);
                    delete (aux);
                    ita=agentList.erase(ita);
		}else{
                    ita++;
                }
            }
            sprintf(buffer, "%s%s%d%s%d%s%d%s%d%s%d", SCORE, LIVES, Player.getLives(),
                    FOOD, Player.getItemCounter(FOOD_ITEM_INDEX),
                    MONEY, Player.getItemCounter(MONEY_ITEM_INDEX),
                    KEYS, Player.getItemCounter(KEY_ITEM_INDEX),
                    ENEMIES, Player.getEnemies());
  
            message.SetMessage(buffer);
            message.SetPosX(Camera->GetPosX()+10);
            message.SetPosY(Camera->GetPosY()+20);
            Render();


            do { elapsedTime= glutGet(GLUT_ELAPSED_TIME);
            } while(elapsedTime-time<minElapsedTime);
        }


	return nextGame;
}

void cGame::Finalize(){
}

//Input
void cGame::ReadKeyboard(unsigned char key, int x, int y, bool press){
	keys[key] = press;
}

void cGame::ReadMouse(int button, int state, int x, int y){
}

//Process
void cGame::Process(){
        bool someKeyPressed=false;
	
	//Process Input
	if(keys[27]){
            nextGame=GAME_OVER_GAME_ST;
        }
	
	if(keys[13]){
            Player.setSuper(true);
        }else if(Player.isSuper()){
            Player.setSuper(false);
        }

	/* if(keys[GLUT_KEY_UP])	... */
        if(keys[GLUT_KEY_UP]){
           
            Player.Jump();
            someKeyPressed=true;
        }
        if(keys[GLUT_KEY_RIGHT]){
            
            Player.GoRight();
            someKeyPressed=true;
        }
        if(keys[GLUT_KEY_LEFT]){
           
            Player.GoLeft();
            someKeyPressed=true;
        }
        if(!someKeyPressed){
            Player.Stop();
        }
}

//Output
void cGame::Render(){
    char buffer[255];
	glClear(GL_COLOR_BUFFER_BIT);
	
	glLoadIdentity();
        
        Camera->Show();

        SceneManager.Draw();
	Player.Draw();

        for(list<cPositionedVisibleObject*>::iterator it=itemList.begin(); it!=itemList.end(); it++){
            (*it)->Draw();
        }

        for(list<cAutonomousAgent*>::iterator it=agentList.begin(); it!=agentList.end(); it++){
            (*it)->Draw();
        }

        //actualiza la puntuación
        message.Draw();

        glutSwapBuffers();
}


